﻿// -----------------------------------------------------------------------
// <copyright file="UserInterface.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Ex03.GarageManagementSystem.ConsoleUI
{
    using System.Reflection;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Ex03.GarageLogic;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public sealed class UserInterface
    {
        private Garage m_Garage = new Garage();

        internal enum eUsersInput
        {
            Yes = 1,
            No = 2
        }

        internal enum eJobToPerform
        {
            Insert = 1,
            LicensePlates = 2,
            ChangeStatus = 3,
            AirInflating = 4,
            ChargeElectric = 5,
            Refuel = 6,
            Display = 7,
            Exit = 8
        }

        public UserInterface()
        {
            StartWorkingGarage();
        }

        private void StartWorkingGarage()
        {
            bool isAppEnd = false;
            while (!isAppEnd)
            {
                GetJobFromUser(ref isAppEnd);
            }
        }

        private void GetJobFromUser(ref bool i_isAppEnd)
        {
            string msg = string.Format(
                @"Hello
Select an option to do in the garage
1. Insert a new car.
2. View a list of license plate numbers of vehicles in the garage.
3. Change the status of a car in the garage.
4. Add max air pressure to all tires.
5. Charge electric vehicle.
6. Refuel a fuel based vehicle.
7. Display data of car in the garage.
8. Exit
");
            Console.Clear();
            Console.WriteLine(msg);
          
            try
            {
              eJobToPerform usersInputEnum = ConvertToEnumJobToDo(Console.ReadLine());
              DoUsersRequest(usersInputEnum, ref i_isAppEnd);
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine(ex.Message + "{0}Press any key to continue...", Environment.NewLine);
                Console.ReadKey();
                Console.Clear();
                GetJobFromUser(ref i_isAppEnd);
            }
        }

        /// <summary>
        /// Performs the chosen job by the user
        /// </summary>
        /// <param name="i_Job">The job the user entered and converted to eJobToPerform enum</param>
        /// <param name="i_isAppEnd">bool variable to set if the user want to exit the application</param>
        private void DoUsersRequest(eJobToPerform i_Job, ref bool i_isAppEnd)
        {
            try
            {
                switch (i_Job)
                {
                    case eJobToPerform.AirInflating:
                        {
                            AirInflatingToMax();
                            break;
                        }

                    case eJobToPerform.ChargeElectric:
                        {
                            ChargeCarBattery();
                            break;
                        }

                    case eJobToPerform.ChangeStatus:
                        {
                            ChangeStatusOfClient();
                            break;
                        }

                    case eJobToPerform.Insert:
                        {
                            InsertNewClient();
                            break;
                        }

                    case eJobToPerform.LicensePlates:
                        {
                            DisplayLicense();
                            break;
                        }

                    case eJobToPerform.Refuel:
                        {
                            RefuelVehicle();
                            break;
                        }

                    case eJobToPerform.Display:
                        {
                            DisplayVehicelData();
                            break;
                        }

                    case eJobToPerform.Exit:
                        {
                            i_isAppEnd = true;
                            break;
                        }
                }
            }
            catch (FormatException exFormatException)
            {
                Console.WriteLine(exFormatException.Message);
            }
            catch (ArgumentException exArgException)
            {
                Console.WriteLine(exArgException.Message);
            }
            catch (ValueOutOfRangeException exValOutOfRangeException)
            {
                Console.WriteLine(exValOutOfRangeException.Message);
                Console.WriteLine("Values are between {0} and {1}", exValOutOfRangeException.MinValue, exValOutOfRangeException.MaxValue);
            }
            catch (System.Exception exParseException)
            {
                Console.WriteLine(exParseException.Message);
            }
            finally
            {
                if (!i_isAppEnd)
                {
                    System.Console.WriteLine("{0}Press any key to continue...", Environment.NewLine);
                    System.Console.ReadKey();
                    GetJobFromUser(ref i_isAppEnd);   
                }
            }
        }

        private void DisplayLicense()
        {
            Console.WriteLine("Do you want to filter the license plates by status?{0}", Environment.NewLine);
            printAllEnumNames<eUsersInput>();

            eUsersInput usersInputEnum = ConvertToEnumUserInput(Console.ReadLine());
            if (usersInputEnum == eUsersInput.No)
            {
                PrintAllLicense();
            }
            else if (usersInputEnum == eUsersInput.Yes)
            {
                Console.WriteLine("Insert status to print:{0}", Environment.NewLine);
                printAllEnumNames<eVehicleStatus>();
                PrintAllLicense(ConvertToEnumTypeStatus(Console.ReadLine()));
            }
        }

        private void PrintAllLicense()
        {
            List<string> licenseToPrint = m_Garage.LicensePlateOfCurrentVehicles();
            int countOfCars = 1;
            Console.WriteLine("Listing the requested license plates:{0}", Environment.NewLine);

            foreach (string currentLicense in licenseToPrint)
            {
                Console.WriteLine("{0}. {1}{2}", countOfCars, currentLicense, Environment.NewLine);
                countOfCars++;
            }
        }

        private void PrintAllLicense(eVehicleStatus currentStatus)
        {
            List<string> licenseToPrint = m_Garage.LicensePlateOfCurrentVehicles(currentStatus);
            int countOfCars = 1;
            Console.WriteLine("Listing the requested license plates filtered by status: {0}{1}", currentStatus.ToString(), Environment.NewLine);

            foreach (string currentLicense in licenseToPrint)
            {
                Console.WriteLine("{0}. {1}{2}", countOfCars, currentLicense, Environment.NewLine);
                countOfCars++;
            }
        }

        private void DisplayVehicelData()
        {
            StringBuilder vehicleDataString = new StringBuilder();
            string plateNumberString = getPlateNumberFromUser();
            m_Garage.GetVehicleData(plateNumberString, ref vehicleDataString);
            System.Console.WriteLine(vehicleDataString);
        }

        private void ChargeCarBattery()
        {
            string plateNumberString = getPlateNumberFromUser();
            bool parseIsSuccess = false;
            float minutesToCharge = 0;

            do
            {
                System.Console.WriteLine("Enter minutes to charge: ");
                string minutesToChargeString = System.Console.ReadLine();
                parseIsSuccess = float.TryParse(minutesToChargeString, out minutesToCharge);

                if (parseIsSuccess == false)
                {
                    Console.WriteLine("{0}Please try again: ", Environment.NewLine);
                }
            }
            while (parseIsSuccess == false);

            m_Garage.ChargeCarBattery(plateNumberString, (float)(minutesToCharge / 60));
            System.Console.WriteLine("Battery has been charged!{0}", Environment.NewLine);
        }

        private void RefuelVehicle()
        {
            float amountToRefuel;
            bool parseIsSuccess = false;
            string plateNumberString = getPlateNumberFromUser();

            Console.WriteLine("Enter type of fuel:{0}", Environment.NewLine);
            printAllEnumNames<eTypeOfFuel>();
            eTypeOfFuel typeFuel = ConvertToEnumTypeFuel(Console.ReadLine());

            System.Console.WriteLine("Enter the amount you want to fill: ");
            parseIsSuccess = float.TryParse(Console.ReadLine(), out amountToRefuel);
            if (parseIsSuccess == false)
            {
                throw new FormatException("Invalid value has been entered");
            }

            m_Garage.RefuelCar(plateNumberString, typeFuel, amountToRefuel);
            System.Console.WriteLine("Tank has been refueled!{0}", Environment.NewLine);
        }

        private eJobToPerform ConvertToEnumJobToDo(string i_UserJob)
        {
            eJobToPerform usersInputEnum = (eJobToPerform)Enum.Parse(typeof(eJobToPerform), i_UserJob);
            if (Enum.IsDefined(typeof(eJobToPerform), usersInputEnum) == false)
            {
                throw new ArgumentException("There is no such option");
            }

            return usersInputEnum;
        }

        private eSupportedVehicleTypes ConvertToEnumSupportedVehicleTypes(string i_SupportedTypesString)
        {
            eSupportedVehicleTypes usersInputEnum = (eSupportedVehicleTypes)Enum.Parse(typeof(eSupportedVehicleTypes), i_SupportedTypesString);
            if (Enum.IsDefined(typeof(eSupportedVehicleTypes), usersInputEnum) == false)
            {
                throw new ArgumentException("There is no such option");
            }

            return usersInputEnum;
        }

        private eUsersInput ConvertToEnumUserInput(string i_UserInput)
        {
            eUsersInput usersInputEnum = (eUsersInput)Enum.Parse(typeof(eUsersInput), i_UserInput);
            if (Enum.IsDefined(typeof(eUsersInput), usersInputEnum) == false)
            {
                throw new ArgumentException("There is no such option");
            }

            return usersInputEnum;
        }

        private eTypeOfFuel ConvertToEnumTypeFuel(string i_fuleType)
        {
            eTypeOfFuel usersInputEnum = (eTypeOfFuel)Enum.Parse(typeof(eTypeOfFuel), i_fuleType);
            if (Enum.IsDefined(typeof(eTypeOfFuel), usersInputEnum) == false)
            {
                throw new ArgumentException("There is no such option");
            }

            return usersInputEnum;
        }

        private eVehicleStatus ConvertToEnumTypeStatus(string i_VehicelStatus)
        {
            eVehicleStatus usersInputEnum = (eVehicleStatus)Enum.Parse(typeof(eVehicleStatus), i_VehicelStatus);
            if (Enum.IsDefined(typeof(eVehicleStatus), usersInputEnum) == false)
            {
                throw new ArgumentException("There is no such option");
            }

            return usersInputEnum;
        }

        private void printAllEnumNames<T>()
        {
            var names = Enum.GetNames(typeof(T));
            int i = 1;

            foreach (var name in names)
            {
                System.Console.WriteLine("{0}. " + name.ToString() + "{1}", i.ToString(), Environment.NewLine);
                i++;
            }
        }

        private void InsertNewClient()
        {
            bool breakOperation = false;
            bool parseSuccess = false;
            bool conversionSucceeded = false;
            Vehicle newVehicle = null;
            string plateNumberString = getPlateNumberFromUser();
         
            bool vehicleIsInDataBase;
            vehicleIsInDataBase = m_Garage.ReturnIfExistingVehicleAndPutBackInRepairState(plateNumberString);
            if (vehicleIsInDataBase == false)
            {
                float currentEnergyLevel = 0;
                string vehicleModel = string.Empty;
                string currentEnergyLevelStr = string.Empty;
                string tiresManufacturer = string.Empty;
                string currentAirPressureStr = string.Empty;
                float currentAirPressure = 0;

                eEngineTypes engineType = eEngineTypes.ElectricEngine;
                eSupportedVehicleTypes vehicleType = eSupportedVehicleTypes.PrivateCar;

                do
                {
                    try
                    {
                        vehicleType = (eSupportedVehicleTypes)UserChoiceFunctionality.GetUserChoiceFromMenu("vehicle type", typeof(eSupportedVehicleTypes));

                        System.Console.WriteLine("Type in Vehicle model:");
                        vehicleModel = System.Console.ReadLine();
                        engineType = (eEngineTypes)UserChoiceFunctionality.GetUserChoiceFromMenu("engine type", typeof(eEngineTypes));
                        System.Console.WriteLine("Type in current energy level (Fuel/Battery time):");
                        currentEnergyLevelStr = System.Console.ReadLine();
                        parseSuccess = float.TryParse(currentEnergyLevelStr, out currentEnergyLevel);
                        if (parseSuccess == false)
                        {
                            Console.WriteLine("You entered wrong data, Try again: ");
                            continue;
                        }

                        System.Console.WriteLine("Type in tires manufacturer:");
                        tiresManufacturer = System.Console.ReadLine();
                        System.Console.WriteLine("Type in current tires air pressure:");
                        currentAirPressureStr = System.Console.ReadLine();
                        parseSuccess = float.TryParse(currentAirPressureStr, out currentAirPressure);
                        if (parseSuccess == false)
                        {
                            Console.WriteLine("you entered wrong data, try again");
                            continue;
                        }

                        conversionSucceeded = true;
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("You didn't write numbers when you were asked. Please try again: {0}", Environment.NewLine);
                        conversionSucceeded = false;
                    }
                    catch (ArgumentException)
                    {
                        Console.WriteLine("You wrote wrong value. Please choose again: {0}", Environment.NewLine);
                        conversionSucceeded = false;
                    }
                } 
                while (conversionSucceeded == false);
                try
                {
                    switch (vehicleType)
                    {
                        case eSupportedVehicleTypes.PrivateCar:
                            {
                                eColor carColor = (eColor)UserChoiceFunctionality.GetUserChoiceFromMenu("car color", typeof(eColor));
                                eNumOfDoors carNumOfDoors = (eNumOfDoors)UserChoiceFunctionality.GetUserChoiceFromMenu("number of doors", typeof(eNumOfDoors));
                                newVehicle = CreateCar.CreateNewPrivateCar(vehicleModel, plateNumberString, engineType, currentEnergyLevel, tiresManufacturer, currentAirPressure, carColor, carNumOfDoors);
                                break;
                            }

                        case eSupportedVehicleTypes.Motorcycle:
                            {
                                int engineCapacity = 0;
                                conversionSucceeded = false;
                                do
                                {
                                    System.Console.WriteLine("Type in motorcycle engine capacity:");
                                    string engineCapacityStr = System.Console.ReadLine();
                                    conversionSucceeded = int.TryParse(engineCapacityStr, out engineCapacity);
                                    if (conversionSucceeded == false)
                                    {
                                        Console.WriteLine("You entered bad number. Please try again: {0}", Environment.NewLine);
                                    }
                                } 
                                while (conversionSucceeded == false);
                                eLicenseType motorcycleLicenseType = (eLicenseType)UserChoiceFunctionality.GetUserChoiceFromMenu("motorcycle license type", typeof(eLicenseType));
                                newVehicle = CreateCar.CreateNewMotorcycle(vehicleModel, plateNumberString, engineType, currentEnergyLevel, tiresManufacturer, currentAirPressure, engineCapacity, motorcycleLicenseType);
                                break;
                            }

                        case eSupportedVehicleTypes.Truck:
                            {
                                int volCapacity = 0;
                                do
                                {
                                    System.Console.WriteLine("Type in volume capacity in the truck:");
                                    string volCapacityStr = System.Console.ReadLine();
                                    conversionSucceeded = int.TryParse(volCapacityStr, out volCapacity);
                                    if (conversionSucceeded == false)
                                    {
                                        Console.WriteLine("You entered bad number. Please try again:{0}", Environment.NewLine);
                                    }
                                } 
                                while (conversionSucceeded == false);
                                System.Console.WriteLine("Is contain hazardous materials?:");
                                eYesOrNoOptions containHazardousMaterials = (eYesOrNoOptions)UserChoiceFunctionality.GetUserChoiceFromMenu("filter option", typeof(eYesOrNoOptions));
                                bool isHazardousMaterials = false;
                                if (containHazardousMaterials == eYesOrNoOptions.Yes)
                                {
                                    isHazardousMaterials = true;
                                }

                                try
                                {
                                    newVehicle = CreateCar.CreateNewTruck(vehicleModel, plateNumberString, engineType, currentEnergyLevel, tiresManufacturer, currentAirPressure, volCapacity, isHazardousMaterials);
                                }
                                catch (ArgumentException exArgException)
                                {
                                    Console.WriteLine(exArgException.Message);
                                    breakOperation = true;
                                }

                                break;
                            }
                    }

                    if (breakOperation == false)
                    {
                        System.Console.WriteLine("Type in owner name:");
                        string ownerName = System.Console.ReadLine();
                        System.Console.WriteLine("Type in owner phone number:");
                        string ownerPhone = System.Console.ReadLine();
                        VehicleInGarage vehicleToPutInGarageData = new VehicleInGarage(newVehicle, ownerName, ownerPhone);
                        m_Garage.AddVehicle(vehicleToPutInGarageData);
                    }
                }
                catch (ValueOutOfRangeException exValOutOfRangeException)
                {
                    Console.WriteLine(exValOutOfRangeException.Message);
                    Console.WriteLine("Values are between {0} and {1}", exValOutOfRangeException.MinValue, exValOutOfRangeException.MaxValue);
                }
            }
            else
            {
                System.Console.WriteLine("Vehicle exists in database and now set to: InRepair!..");
            }
        }

        private void ChangeStatusOfClient()
        {
            try
            {
                string plateNumberString = getPlateNumberFromUser();
                Console.WriteLine("Insert The new status of the vehicle:{0}", Environment.NewLine);
                printAllEnumNames<eVehicleStatus>();

                eVehicleStatus newStatus = ConvertToEnumTypeStatus(Console.ReadLine());
                m_Garage.ChangeCarStatus(plateNumberString, newStatus);
                System.Console.WriteLine("Status has been changed!{0}", Environment.NewLine);
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine(ex.Message);
            }  
        }

        private void AirInflatingToMax()
        {
            string plateNumberString = getPlateNumberFromUser();
            m_Garage.AddMaxAirPressure(plateNumberString);
            System.Console.WriteLine("Air pressure has been added!{0}", Environment.NewLine);
        }

        private string getPlateNumberFromUser()
        {
            Console.WriteLine("Insert the license plate number of the vehicle.");
            string licenseFromUser = Console.ReadLine();

            return licenseFromUser;
        }
    }
}
